#include "./SYSTEM/sys_param/sys_param.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/24CXX/bsp_24cxx.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/**
 * @brief 参数保存/加载方式选择
 * @details 0: 使用一次性完成参数保存/加载
 *          1: 使用分次完成参数保存/加载
 */
#define USE_PARAM_SAVE_LOAD_API 0

/******************************************  EEPROM储存设备操作  ******************************************************/
static uint32_t sg_length = 0;  /* 存储设备的数据长度 */
#define PARAM_VERSION 0x00      /* 参数版本号 */
#define PARAM_VERSION_ADDR 0    /* 参数版本号在EEPROM中的存储地址 */
#define PARAM_LENGTH_ADDR 1     /* 数据长度在EEPROM中的存储地址 */
#define PARAM_DATA_START_ADDR 5 /* 参数数据在EEPROM中的起始地址 */

/* 全局参数实例定义 */
DisplayParam_t g_tDisplayParam = 
{
    .direction = 0,    /* 默认0度方向 */
    .brightness = 100, /* 默认亮度100% */
    .language = 0,     /* 默认中文 */
};

/* 参数管理器实例 */
static cotParamManager_t sg_tParamManager;

/* 参数表定义 */
static cotParamInfo_t sg_tParamTable[] =
{
    /* 显示参数 */
    COT_PARAM_ITEM_BIND(1, g_tDisplayParam.direction, COT_PARAM_UINT8, COT_PARAM_ATTR_WR, 0, 0, 3),
    COT_PARAM_ITEM_BIND(2, g_tDisplayParam.brightness, COT_PARAM_UINT8, COT_PARAM_ATTR_WR, 100, 20, 100),
    COT_PARAM_ITEM_BIND(3, g_tDisplayParam.language, COT_PARAM_UINT8, COT_PARAM_ATTR_WR, 0, 0, 1),
};

/**
 * @brief 参数加载回调函数
 * @param pBuf 数据缓冲区指针
 * @param bufSize 缓冲区大小
 * @param pLength 实际读取的数据长度
 * @return 0:成功 其他:失败
 * @details 从存储设备读取数据，支持分段读取
 */
int OnLoadCallback(uint8_t *pBuf, uint16_t bufSize, uint16_t *pLength)
{
    /* 用于存储本次实际读取的数据长度 */
    uint16_t length;
#if USE_PARAM_SAVE_LOAD_API
    /* 静态变量记录当前读取位置的偏移量，用于支持分段读取 */
    static uint32_t s_offset = 0;
#else
    /* 一次性读取不需要保持偏移量 */
    uint32_t s_offset = 0;
#endif
    /* 检查参数有效性 */
    if(pBuf == NULL || pLength == NULL)
    {
        return -1;
    }

    /* 检查是否还有数据可读 */
    if (s_offset < sg_length)
    {
        /* 判断剩余数据长度是否足够填满缓冲区 */
        if (s_offset + bufSize <= sg_length)
        {
            /* 剩余数据足够，按缓冲区大小读取 */
            length = bufSize;
        }
        else
        {
            /* 剩余数据不足，读取剩余全部数据 */
            length = sg_length - s_offset;
        }

        /* 从EEPROM读取数据（考虑参数版本号的偏移） */
        at24cxx_read(s_offset + PARAM_DATA_START_ADDR, pBuf, length);
        s_offset += length;
    }
    else
    {
        /* 已无数据可读，重置状态并返回长度0 */
        length = 0;
        s_offset = 0;
        printf("OnLoadCallback: Read complete, total length=%lu\n", (unsigned long)sg_length);
    }

    if (length > 0)
    {
        printf("------------------------------------------------------------------------------------------\n");
        HEX_PRINTF("\tread", pBuf, length);
        printf("------------------------------------------------------------------------------------------\n");
    }

    /* 返回实际读取的长度 */
    *pLength = length;
    return 0;
}

/**
 * @brief 参数保存回调函数
 * @param pBuf 数据缓冲区指针
 * @param len 数据长度
 * @return 0:成功 其他:失败
 * @details 向存储设备写入数据，支持分段写入
 *          当len为0时表示写入完成，会打印完整的存储数据
 */
int OnSaveCallback(const uint8_t *pBuf, uint16_t len)
{
    /* 静态变量记录当前写入位置的偏移量，用于支持分段写入 */
    static uint32_t s_offset = 0;
    
    /* 检查参数有效性 */
    if(pBuf == NULL && len > 0)
    {
        return -1;
    }
    
    /* 检查是否有数据需要写入 */
    if (len > 0)
    {
        /* 检查写入地址是否超出EEPROM容量 */
        if (s_offset + len > EE_TYPE)
        {
            return -1;
        }

        /* 将数据写入EEPROM（考虑参数版本号的偏移） */
        at24cxx_write(s_offset + PARAM_DATA_START_ADDR, (uint8_t *)pBuf, len);
        
        /* 打印写入的数据内容 */
        HEX_PRINTF("\twrite", pBuf, len);
        
        /* 更新写入位置和总数据长度 */
        s_offset += len;
        sg_length = s_offset;
    }
    else
    {
        /* len为0表示写入完成，保存总数据长度到EEPROM */
        at24cxx_write(PARAM_LENGTH_ADDR, (uint8_t *)&sg_length, sizeof(sg_length));
        printf("OnSaveCallback: Save complete, total length=%lu\n", (unsigned long)sg_length);
        
        /* 重置写入位置，为下次写入做准备 */
        s_offset = 0;
    }

    return 0;
}

/**
 * @brief 参数初始化和管理函数
 * @param isReset 是否重置参数
 * @details 初始化参数管理器，如果存储设备中没有参数数据则进行首次保存
 *          然后重新加载参数
 */
void InitParam(bool isReset)
{
    uint8_t version = PARAM_VERSION;
    uint8_t stored_version = 0;
    
    /* 初始化24C02 */
    at24cxx_init();
    
    /* 检查24C02是否正常工作 */
    if (at24cxx_check() != 0)
    {
        printf("24C02 check failed!\n");
        return;
    }
    
    /* 初始化参数管理器 */
    cotParam_Init(&sg_tParamManager, sg_tParamTable, COT_PARAM_TABLE_SIZE(sg_tParamTable));
    
    /* 读取EEPROM中的参数版本号 */
    at24cxx_read(PARAM_VERSION_ADDR, &stored_version, sizeof(stored_version));
        
    /* 读取存储的数据长度 */
    at24cxx_read(PARAM_LENGTH_ADDR, (uint8_t *)&sg_length, sizeof(sg_length));
    
    /* 检查参数版本号是否匹配 */
    if (stored_version != version)
    {
        /* 参数版本不匹配或强制重置，需要初始化并保存参数 */
        printf("Parameter version mismatch or reset requested, initializing parameters...\n");
        
        /* 写入新的参数版本号 */
        at24cxx_write(PARAM_VERSION_ADDR, (uint8_t *)&version, sizeof(version));
        
        /* 保存默认参数到EEPROM */
        SaveParam(false);
    }

    /* 从EEPROM加载参数 */
    ReloadParam(isReset);
}

/**
 * @brief 参数校验错误时的处理函数
 * @param pParamInfo 参数信息指针
 * @param eCheckResult 校验结果
 * @return 0:成功
 * @details 打印错误参数信息并重置为默认值
 */
int OnCheckErrorResetHandle(const cotParamInfo_t *pParamInfo, cotParamCheckRet_e eCheckResult)
{
    printf("\n--------error list start ----------\n");
    printf("%4s    %-24s %-8s %-8s %-8s %-10s %-10s %-10s %-10s\n", "id", "name", "type", "length", "attr", "val", "def", "min", "max");
    ShowSingleParam(pParamInfo);
    printf("--------error list end ------------\n");
    cotParam_SingleParamResetDefValue(pParamInfo);
    return 0;
}

/**
 * @brief 重新加载参数
 * @param isReset 是否在加载前进行参数校验
 * @details 从存储设备中重新加载参数值
 *          支持两种加载方式：分次加载和一次性加载
 */
void ReloadParam(bool isReset)
{
    printf("\n===================================== load param start ===================================\n");

    if (isReset)
    {
        cotParam_Check(&sg_tParamManager, OnCheckErrorResetHandle);
    }

#if USE_PARAM_SAVE_LOAD_API
    cotParam_Load(&sg_tParamManager, OnLoadCallback);
#else
    uint8_t buf[EE_TYPE + 1];
    uint16_t length = 0;    
    /* 从EEPROM读取数据到缓冲区 */
    OnLoadCallback(buf, sizeof(buf), &length);
    /* 反序列化参数数据 */
    cotParam_Deserialization(&sg_tParamManager, buf, length);
#endif
    printf("===================================== load param end =====================================\n\n");
}

/**
 * @brief 保存参数到存储设备
 * @param isReset 是否在保存前进行参数校验
 * @details 将参数值保存到存储设备中
 *          支持两种保存方式：分次保存和一次性保存
 */
void SaveParam(bool isReset)
{
    printf("\n===================================== save param start ===================================\n");
    /* 如果需要在保存前进行参数校验 */
    if (isReset)
    {
        /* 调用参数校验函数，如果校验失败会通过OnCheckErrorResetHandle回调函数处理 */
        cotParam_Check(&sg_tParamManager, OnCheckErrorResetHandle);
    }
#if USE_PARAM_SAVE_LOAD_API
    /* 使用分次保存API，通过OnSaveCallback回调函数将数据写入存储设备 */
    cotParam_Save(&sg_tParamManager, OnSaveCallback);
#else
    /* 使用一次性保存方式：
     * 1. 先将所有参数序列化到临时缓冲区
     * 2. 调用OnSaveCallback保存序列化后的数据
     * 3. 最后调用OnSaveCallback(buf,0)表示保存完成
     */
    uint8_t buf[EE_TYPE + 1];       /* 定义一个临时缓冲区，用于存储序列化后的参数数据 */
    uint32_t bufLength = 0; /* 用于记录序列化后的数据长度 */
    bufLength = cotParam_Serialize(&sg_tParamManager, buf);
    
    /* 先写入数据长度信息 */
    at24cxx_write(PARAM_LENGTH_ADDR, (uint8_t *)&bufLength, sizeof(bufLength));
    /* 写入实际数据 */
    OnSaveCallback(buf, bufLength);
    OnSaveCallback(buf, 0);
#endif
    printf("===================================== save param end =====================================\n\n");
}

/**
 * @brief 重置所有参数为默认值
 */
void ResetParam(void)
{
    printf("reset param\n");
    cotParam_ResetDefault(&sg_tParamManager);
}

/**
 * @brief 显示所有参数信息函数
 * @details 以表格形式显示所有参数的详细信息
 *          表格内容包括：参数ID、名称、类型、长度、属性、当前值、默认值、最小值、最大值
 *          通过迭代器遍历参数列表，逐个显示每个参数的信息
 *          对于不同类型的参数，使用不同的格式化方式显示
 */
void ShowAllParam(void)
{
    /* 用于遍历参数列表的索引变量 */
    size_t idx = 0;
    /* 用于存储当前遍历到的参数信息 */
    cotParamInfo_t *paramInfo;

    /* 打印表头，包含参数的各个属性字段名称
     * id: 参数ID
     * name: 参数名称
     * type: 参数类型
     * length: 参数长度
     * attr: 参数属性
     * val: 当前值
     * def: 默认值
     * min: 最小值
     * max: 最大值
     */
    printf("\n===================================== Show param start ===================================\n");
    printf("%4s    %-24s %-8s %-8s %-8s %-10s %-10s %-10s %-10s\n", "id", "name", "type", "length", "attr", "val", "def", "min", "max");

    /* 遍历参数列表，打印每个参数的详细信息
     * cotParam_IterateList函数每次返回一个参数信息
     * 当返回NULL时表示遍历结束
     */
    do
    {
        paramInfo = cotParam_IterateList(&sg_tParamManager, &idx);
        /* 调用单个参数显示函数打印当前参数的详细信息 */
        ShowSingleParam(paramInfo);
    } while (paramInfo != NULL);
    printf("===================================== Show param end =====================================\n\n");
}

/**
 * @brief 计算参数在EEPROM中的起始地址
 * @param id 参数ID
 * @return uint32_t 参数在EEPROM中的起始地址
 */
static uint32_t GetParamStartAddr(uint16_t id)
{
    uint32_t addr = PARAM_DATA_START_ADDR + 1;  /* 从PARAM_DATA_START_ADDR+1开始，跳过COT_PARAM_SUPPORT_NUM字节 */
    
    /* 遍历参数表直到找到目标ID */
    for(uint16_t i = 0; i < sg_tParamManager.count; i++)
    {
        if(sg_tParamManager.pParamTable[i].id == id)
        {
            printf("Found param ID %d at index %d, addr: %lu\n", id, i, (unsigned long)addr);
            return addr;
        }
        
        /* 计算下一个参数的起始地址 */
#if COT_PARAM_USE_KEY_VALUE
        /* 键值对模式: 键长度 + 参数长度 */
        addr += COT_PARAM_SUPPORT_NUM + sg_tParamManager.pParamTable[i].length;
//        printf("Param %d: ID=%d, length=%d, next addr=%lu\n", 
//               i, 
//               sg_tParamManager.pParamTable[i].id,
//               sg_tParamManager.pParamTable[i].length,
//               addr);
#else
        /* 连续模式: 仅参数长度 */
        addr += sg_tParamManager.pParamTable[i].length;
//        printf("Param %d: ID=%d, length=%d, next addr=%lu\n", 
//               i, 
//               sg_tParamManager.pParamTable[i].id,
//               sg_tParamManager.pParamTable[i].length,
//               addr);
#endif
    }
    
    return 0; /* 未找到参数 */
}

/**
 * @brief 保存单个参数到EEPROM
 * @param pParam 参数信息结构体指针
 * @return int 0:成功; -1:失败
 */
static int SaveSingleParam(const cotParamInfo_t *pParam)
{
    uint8_t buf[sizeof(cotParamInfo_t) + COT_PARAM_STRING_MAX_LENGTH];
    uint8_t *ptr = buf;
    uint32_t startAddr;
    uint16_t length;
    
    /* 参数有效性检查 */
    if(pParam == NULL)
    {
        return -1;
    }
    
    /* 获取参数在EEPROM中的起始地址 */
    startAddr = GetParamStartAddr(pParam->id);
    if(startAddr == 0)
    {
        return -1;
    }
    
//    printf("Saving param ID %d at addr %lu\n", pParam->id, startAddr);
    
#if COT_PARAM_USE_KEY_VALUE
    /* 构造键值 */
    uint64_t key = 0;
#if COT_PARAM_SUPPORT_NUM == COT_PARAM_SUPPORT_16
    key = (pParam->id << 4) | (pParam->length & 0x0F);
#elif COT_PARAM_SUPPORT_NUM == COT_PARAM_SUPPORT_256
    key = (pParam->id << 8) | (pParam->length & 0xFF);
#elif COT_PARAM_SUPPORT_NUM == COT_PARAM_SUPPORT_4096
    key = (pParam->id << 12) | (pParam->length & 0xFFF);
#endif
    /* 序列化键值 */
    ptr = SerializeUint(ptr, key, COT_PARAM_SUPPORT_NUM);
//    printf("Serialized key: 0x%llx\n", key);
#endif

    /* 序列化参数值 */
    length = ParamInfoToStream(ptr, (cotParamInfo_t *)pParam);
//    printf("Serialized param length: %d\n", length);
    
    /* 写入EEPROM */
    at24cxx_write(startAddr, buf, length + (COT_PARAM_USE_KEY_VALUE ? COT_PARAM_SUPPORT_NUM : 0));
    
    return 0;
}

int SaveSingleParamByAddr(const void *pParam)
{
    const cotParamInfo_t *pParamInfo;
    
    /* 参数有效性检查 */
    if (pParam == NULL)
    {
        return -1;
    }
    
    /* 查找参数信息 */
    pParamInfo = cotParam_FindParamByParamPtr(&sg_tParamManager, pParam);
    if (pParamInfo == NULL)
    {
        return -1;
    }
    
    /* 打印参数保存信息 */
    printf("Save param: ID=%d, Name=%s, Type=", pParamInfo->id, (char *)pParamInfo->name.pTextString);
    
    /* 根据不同类型打印参数值 */
    switch (pParamInfo->type)
    {
        case COT_PARAM_INT8:
            printf("INT8, Value=%d\r\n", *(int8_t *)pParam);
            break;
        case COT_PARAM_UINT8:
            printf("UINT8, Value=%u\r\n", *(uint8_t *)pParam);
            break;
        case COT_PARAM_INT16:
            printf("INT16, Value=%d\r\n", *(int16_t *)pParam);
            break;
        case COT_PARAM_UINT16:
            printf("UINT16, Value=%u\r\n", *(uint16_t *)pParam);
            break;
        case COT_PARAM_INT32:
            printf("INT32, Value=%ld\r\n", *(long *)pParam);
            break;
        case COT_PARAM_UINT32:
            printf("UINT32, Value=%lu\r\n", *(unsigned long *)pParam);
            break;
        case COT_PARAM_FLOAT:
            printf("FLOAT, Value=%.3f\r\n", *(float *)pParam);
            break;
        case COT_PARAM_DOUBLE:
            printf("DOUBLE, Value=%.3lf\r\n", *(double *)pParam);
            break;
        case COT_PARAM_STRING:
            printf("STRING, Value=%s\r\n", (char *)pParam);
            break;
        default:
            printf("UNKNOWN\r\n");
            break;
    }
    
    /* 调用原有的保存函数 */
    return SaveSingleParam(pParamInfo);
}

/* 通过参数地址加载单个参数 */
int LoadSingleParamByAddr(const void *pParam)
{
    const cotParamInfo_t *pParamInfo = cotParam_FindParamByParamPtr(&sg_tParamManager, pParam);
    if (pParamInfo == NULL)
    {
        return -1;
    }

    uint32_t addr = GetParamStartAddr(pParamInfo->id);
    if (addr == 0)
    {
        return -1;
    }

    /* 分配足够大的缓冲区，包括键值和参数数据 */
    uint8_t buf[sizeof(cotParamInfo_t) + COT_PARAM_STRING_MAX_LENGTH];
    uint16_t totalLength = pParamInfo->length;

#if COT_PARAM_USE_KEY_VALUE
    /* 在键值对模式下，需要额外读取键值 */
    totalLength += COT_PARAM_SUPPORT_NUM;
#endif

    /* 读取参数数据 */
    at24cxx_read(addr, buf, totalLength);

#if COT_PARAM_USE_KEY_VALUE
    /* 跳过键值部分 */
    uint8_t *dataPtr = buf + COT_PARAM_SUPPORT_NUM;
#else
    uint8_t *dataPtr = buf;
#endif

    /* 反序列化参数值 */
    ParamInfoFormStream((cotParamInfo_t *)pParamInfo, dataPtr);

    return 0;
}

/* 单个参数校验处理函数 */
int SingleParamCheckProcess(const void *pCurParam, cotParamResetOpt_e eResetOpt)
{
    /* 查找参数信息 */
    const cotParamInfo_t *pParamInfo = cotParam_FindParamByParamPtr(&sg_tParamManager, pCurParam);
    
    /* 校验参数并根据eResetOpt处理超限值 */
    return cotParam_SingleParamCheckProcess(pParamInfo, eResetOpt);
}

/* 单个参数输入值校验函数 */
cotParamCheckRet_e SingleParamCheck(const void *pCurParam, const void *pCheckValue)
{
    cotParamCheckRet_e eCheckResult;
    const cotParamInfo_t *pParamInfo = cotParam_FindParamByParamPtr(&sg_tParamManager, pCurParam);
    
    /* 检查类型匹配、范围限制和自定义校验 */
    cotParam_SingleParamCheckInput(pParamInfo, pCheckValue, &eCheckResult);
    
    return eCheckResult;
}

/* 单个参数自检函数 */
cotParamCheckRet_e SingleParamSelfCheck(const void *pCurParam)
{
    cotParamCheckRet_e eCheckResult;
    const cotParamInfo_t *pParamInfo = cotParam_FindParamByParamPtr(&sg_tParamManager, pCurParam);
    
    /* 检查范围限制和自定义校验 */
    cotParam_SingleParamSelfCheck(pParamInfo, &eCheckResult);
    
    return eCheckResult;
}

/* 单个参数重置为默认值函数 */
void SingleParamResetResetDefValue(const void *pCurParam)
{
    const cotParamInfo_t *pParamInfo = cotParam_FindParamByParamPtr(&sg_tParamManager, pCurParam);
    cotParam_SingleParamResetDefValue(pParamInfo);
}

/**
 * @brief 单个参数修改函数
 * @param pCurParam 待修改的参数指针
 * @param ... 可变参数，新的参数值
 * @details 修改单个参数的值，支持所有参数类型
 *          使用可变参数方式传入新的参数值，方便调用
 *          内部通过参数管理器的实现函数完成实际的参数修改
 *          修改前会进行参数校验，确保新值满足参数的约束条件
 * @note 使用示例：
 *       SingleParamChange(&g_tTestVal.uiValue, 1500);
 *       SingleParamChange(g_tTestVal.szString_1, "test");
 */
void SingleParamChange(const void *pCurParam, ...)
{
    /* 定义可变参数列表变量，用于处理不定数量的参数 */
    va_list paramList;

    /* 初始化可变参数列表，pCurParam为最后一个固定参数，之后的参数都通过paramList访问 */
    va_start(paramList, pCurParam);

    /* 调用框架提供的参数修改实现函数，该函数会根据参数类型自动解析可变参数列表中的值，并在修改参数值之前进行参数校验，确保修改后的值满足约束条件 */
    cotParam_SingleParamChangeImpl(&sg_tParamManager, pCurParam, paramList);

    /* 清理可变参数列表，释放相关资源 */
    va_end(paramList);
}

/**
 * @brief 获取参数信息
 * @param pParam 参数指针
 * @return const cotParamInfo_t* 返回参数信息结构体指针，如果未找到则返回NULL
 */
const cotParamInfo_t* GetParamInfo(const void* pParam)
{
    /* 参数有效性检查 */
    if (pParam == NULL)
    {
        return NULL;
    }
    
    /* 查找参数信息 */
    return cotParam_FindParamByParamPtr(&sg_tParamManager, pParam);
}
